import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';

part 'tunnel_models.g.dart';

/// 隧道数据模型
@JsonSerializable()
class TunnelData {
  const TunnelData({
    required this.tunnelId,
    required this.serverName,
    required this.localHost,
    required this.localPort,
    required this.tunnelHost,
    required this.tunnelPort,
    required this.publicPort,
    required this.publicAddress,
    required this.tunnelType,
    this.isConnected,
    this.status,
    this.createdAt,
    this.updatedAt,
  });

  @JsonKey(name: 'tunnelId')
  final String tunnelId;
  @JsonKey(name: 'serverName')
  final String serverName;
  @JsonKey(name: 'localHost')
  final String localHost;
  @JsonKey(name: 'localPort', fromJson: _parseLocalPort)
  final String localPort;
  @JsonKey(name: 'tunnelHost')
  final String tunnelHost;
  @JsonKey(name: 'tunnelPort')
  final String tunnelPort;
  @JsonKey(name: 'publicPort', fromJson: _parsePublicPort)
  final String publicPort;
  @JsonKey(name: 'public_url')
  final String publicAddress;
  @JsonKey(name: 'tunnel_type')
  final String tunnelType;
  @JsonKey(name: 'isConnected')
  final bool? isConnected;
  final String? status;
  @JsonKey(name: 'createdAt')
  final DateTime? createdAt;
  @JsonKey(name: 'updatedAt')
  final DateTime? updatedAt;

  // 处理后端返回的数字端口转换为字符串
  static String _parseLocalPort(dynamic value) {
    if (value is int) return value.toString();
    if (value is String) return value;
    return '0';
  }

  static String _parsePublicPort(dynamic value) {
    if (value is int) return value.toString();
    if (value is String) return value;
    return '0';
  }

  factory TunnelData.fromJson(Map<String, dynamic> json) => _$TunnelDataFromJson(json);
  Map<String, dynamic> toJson() => _$TunnelDataToJson(this);

  @override
  String toString() => 'TunnelData(tunnelId: $tunnelId, serverName: $serverName, publicAddress: $publicAddress)';
}

/// 创建隧道请求
@JsonSerializable()
class CreateTunnelRequest {
  const CreateTunnelRequest({
    required this.localHost,
    required this.localPort,
    required this.serverName,
    this.tunnelType = 'http',
  });

  @JsonKey(name: 'localHost')
  final String localHost;
  @JsonKey(name: 'localPort')
  final String localPort;
  @JsonKey(name: 'serverName')
  final String serverName;
  @JsonKey(name: 'tunnelType')
  final String tunnelType;

  factory CreateTunnelRequest.fromJson(Map<String, dynamic> json) => _$CreateTunnelRequestFromJson(json);
  Map<String, dynamic> toJson() => _$CreateTunnelRequestToJson(this);
}

/// 创建隧道响应
@JsonSerializable()
class CreateTunnelResponse {
  const CreateTunnelResponse({
    required this.tunnelId,
    required this.tunnelHost,
    required this.tunnelPort,
    required this.publicPort,
  });

  @JsonKey(name: 'tunnelId')
  final String tunnelId;
  @JsonKey(name: 'tunnelHost')
  final String tunnelHost;
  @JsonKey(name: 'tunnelPort')
  final String tunnelPort;
  @JsonKey(name: 'publicPort')
  final String publicPort;

  factory CreateTunnelResponse.fromJson(Map<String, dynamic> json) => _$CreateTunnelResponseFromJson(json);
  Map<String, dynamic> toJson() => _$CreateTunnelResponseToJson(this);
}

/// 隧道列表响应
@JsonSerializable()
class TunnelListResponse {
  const TunnelListResponse({
    required this.tunnels,
  });

  final List<TunnelData> tunnels;

  factory TunnelListResponse.fromJson(Map<String, dynamic> json) => _$TunnelListResponseFromJson(json);
  Map<String, dynamic> toJson() => _$TunnelListResponseToJson(this);
}

/// HTTP请求包
class RequestPacket {
  const RequestPacket({
    required this.requestId,
    required this.method,
    required this.path,
    required this.headers,
    required this.body,
  });

  final String requestId;
  final String method;
  final String path;
  final Map<String, List<String>> headers;
  final List<int> body;

  factory RequestPacket.fromJson(Map<String, dynamic> json) {
    // 安全解析headers
    final Map<String, List<String>> headers = {};
    final headersJson = json['headers'] as Map<String, dynamic>?;
    if (headersJson != null) {
      for (final entry in headersJson.entries) {
        final value = entry.value;
        if (value is List) {
          headers[entry.key] = value.map((e) => e.toString()).toList();
        } else if (value is String) {
          headers[entry.key] = [value];
        }
      }
    }

    // 安全解析body
    final List<int> body = [];
    final bodyJson = json['body'];
    if (bodyJson is List) {
      body.addAll(bodyJson.map((e) => (e as num).toInt()));
    } else if (bodyJson is String) {
      body.addAll(utf8.encode(bodyJson));
    }

    return RequestPacket(
      requestId: json['requestId'] as String? ?? '',
      method: json['method'] as String? ?? 'GET',
      path: json['path'] as String? ?? '/',
      headers: headers,
      body: body,
    );
  }

  Map<String, dynamic> toJson() => {
    'requestId': requestId,
    'method': method,
    'path': path,
    'headers': headers,
    'body': body,
  };
}

/// HTTP响应包
class ResponsePacket {
  const ResponsePacket({
    required this.type,
    required this.requestId,
    required this.status,
    required this.headers,
    required this.body,
  });

  final String type;
  final String requestId;
  final int status;
  final Map<String, List<String>> headers;
  final List<int> body;

  factory ResponsePacket.fromJson(Map<String, dynamic> json) {
    // 安全解析headers
    final Map<String, List<String>> headers = {};
    final headersJson = json['headers'] as Map<String, dynamic>?;
    if (headersJson != null) {
      for (final entry in headersJson.entries) {
        final value = entry.value;
        if (value is List) {
          headers[entry.key] = value.map((e) => e.toString()).toList();
        } else if (value is String) {
          headers[entry.key] = [value];
        }
      }
    }

    // 安全解析body
    final List<int> body = [];
    final bodyJson = json['body'];
    if (bodyJson is List) {
      body.addAll(bodyJson.map((e) => (e as num).toInt()));
    } else if (bodyJson is String) {
      body.addAll(utf8.encode(bodyJson));
    }

    return ResponsePacket(
      type: json['type'] as String? ?? 'response',
      requestId: json['requestId'] as String? ?? '',
      status: json['status'] as int? ?? 200,
      headers: headers,
      body: body,
    );
  }

  Map<String, dynamic> toJson() => {
    'type': type,
    'requestId': requestId,
    'status': status,
    'headers': headers,
    'body': body,
  };
}

/// 隧道连接状态
enum TunnelConnectionStatus {
  @JsonValue('connecting')
  connecting,
  @JsonValue('connected')
  connected,
  @JsonValue('disconnected')
  disconnected,
  @JsonValue('error')
  error,
}

/// 隧道连接状态数据
@JsonSerializable()
class TunnelConnectionState {
  const TunnelConnectionState({
    required this.tunnelId,
    required this.status,
    this.message,
    this.timestamp,
  });

  @JsonKey(name: 'tunnel_id')
  final String tunnelId;
  final TunnelConnectionStatus status;
  final String? message;
  final DateTime? timestamp;

  factory TunnelConnectionState.fromJson(Map<String, dynamic> json) => _$TunnelConnectionStateFromJson(json);
  Map<String, dynamic> toJson() => _$TunnelConnectionStateToJson(this);

  /// 便捷构造函数
  factory TunnelConnectionState.connecting(String tunnelId) => TunnelConnectionState(
        tunnelId: tunnelId,
        status: TunnelConnectionStatus.connecting,
        timestamp: DateTime.now(),
      );

  factory TunnelConnectionState.connected(String tunnelId) => TunnelConnectionState(
        tunnelId: tunnelId,
        status: TunnelConnectionStatus.connected,
        timestamp: DateTime.now(),
      );

  factory TunnelConnectionState.disconnected(String tunnelId, String message) => TunnelConnectionState(
        tunnelId: tunnelId,
        status: TunnelConnectionStatus.disconnected,
        message: message,
        timestamp: DateTime.now(),
      );

  factory TunnelConnectionState.error(String tunnelId, String message) => TunnelConnectionState(
        tunnelId: tunnelId,
        status: TunnelConnectionStatus.error,
        message: message,
        timestamp: DateTime.now(),
      );

  @override
  String toString() => 'TunnelConnectionState(tunnelId: $tunnelId, status: $status, message: $message)';
}